﻿/**
*
* Date picker
* Author: Stefan Petre www.eyecon.ro
* 
* Dual licensed under the MIT and GPL licenses
* 
*/
(function ($) {
    var DatePicker = function () {
        var ids = {},
			views = {
			    years: 'datepickerViewYears',
			    moths: 'datepickerViewMonths',
			    days: 'datepickerViewDays'
			},
			tpl = {
			    wrapper: '<div class="datepicker"><div class="datepickerBorderT" /><div class="datepickerBorderB" /><div class="datepickerBorderL" /><div class="datepickerBorderR" /><div class="datepickerBorderTL" /><div class="datepickerBorderTR" /><div class="datepickerBorderBL" /><div class="datepickerBorderBR" /><div class="datepickerContainer"><table cellspacing="0" cellpadding="0"><tbody><tr></tr></tbody></table></div></div>',
			    head: [
					'<td>',
					'<table cellspacing="0" cellpadding="0">',
						'<thead>',
							'<tr>',
								'<th class="datepickerGoPrev"><a href="#"><span><%=prev%></span></a></th>',
								'<th colspan="6" class="datepickerMonth"><a href="#"><span></span></a></th>',
								'<th class="datepickerGoNext"><a href="#"><span><%=next%></span></a></th>',
							'</tr>',
							'<tr class="datepickerDoW">',
								'<th><span><%=week%></span></th>',
								'<th><span><%=day1%></span></th>',
								'<th><span><%=day2%></span></th>',
								'<th><span><%=day3%></span></th>',
								'<th><span><%=day4%></span></th>',
								'<th><span><%=day5%></span></th>',
								'<th><span><%=day6%></span></th>',
								'<th><span><%=day7%></span></th>',
							'</tr>',
						'</thead>',
					'</table></td>'
				],
			    space: '<td class="datepickerSpace"><div></div></td>',
			    days: [
					'<tbody class="datepickerDays">',
						'<tr>',
							'<th class="datepickerWeek"><a href="#"><span><%=weeks[0].week%></span></a></th>',
							'<td class="<%=weeks[0].days[0].classname%>"><a href="#"><span><%=weeks[0].days[0].text%></span></a></td>',
							'<td class="<%=weeks[0].days[1].classname%>"><a href="#"><span><%=weeks[0].days[1].text%></span></a></td>',
							'<td class="<%=weeks[0].days[2].classname%>"><a href="#"><span><%=weeks[0].days[2].text%></span></a></td>',
							'<td class="<%=weeks[0].days[3].classname%>"><a href="#"><span><%=weeks[0].days[3].text%></span></a></td>',
							'<td class="<%=weeks[0].days[4].classname%>"><a href="#"><span><%=weeks[0].days[4].text%></span></a></td>',
							'<td class="<%=weeks[0].days[5].classname%>"><a href="#"><span><%=weeks[0].days[5].text%></span></a></td>',
							'<td class="<%=weeks[0].days[6].classname%>"><a href="#"><span><%=weeks[0].days[6].text%></span></a></td>',
						'</tr>',
						'<tr>',
							'<th class="datepickerWeek"><a href="#"><span><%=weeks[1].week%></span></a></th>',
							'<td class="<%=weeks[1].days[0].classname%>"><a href="#"><span><%=weeks[1].days[0].text%></span></a></td>',
							'<td class="<%=weeks[1].days[1].classname%>"><a href="#"><span><%=weeks[1].days[1].text%></span></a></td>',
							'<td class="<%=weeks[1].days[2].classname%>"><a href="#"><span><%=weeks[1].days[2].text%></span></a></td>',
							'<td class="<%=weeks[1].days[3].classname%>"><a href="#"><span><%=weeks[1].days[3].text%></span></a></td>',
							'<td class="<%=weeks[1].days[4].classname%>"><a href="#"><span><%=weeks[1].days[4].text%></span></a></td>',
							'<td class="<%=weeks[1].days[5].classname%>"><a href="#"><span><%=weeks[1].days[5].text%></span></a></td>',
							'<td class="<%=weeks[1].days[6].classname%>"><a href="#"><span><%=weeks[1].days[6].text%></span></a></td>',
						'</tr>',
						'<tr>',
							'<th class="datepickerWeek"><a href="#"><span><%=weeks[2].week%></span></a></th>',
							'<td class="<%=weeks[2].days[0].classname%>"><a href="#"><span><%=weeks[2].days[0].text%></span></a></td>',
							'<td class="<%=weeks[2].days[1].classname%>"><a href="#"><span><%=weeks[2].days[1].text%></span></a></td>',
							'<td class="<%=weeks[2].days[2].classname%>"><a href="#"><span><%=weeks[2].days[2].text%></span></a></td>',
							'<td class="<%=weeks[2].days[3].classname%>"><a href="#"><span><%=weeks[2].days[3].text%></span></a></td>',
							'<td class="<%=weeks[2].days[4].classname%>"><a href="#"><span><%=weeks[2].days[4].text%></span></a></td>',
							'<td class="<%=weeks[2].days[5].classname%>"><a href="#"><span><%=weeks[2].days[5].text%></span></a></td>',
							'<td class="<%=weeks[2].days[6].classname%>"><a href="#"><span><%=weeks[2].days[6].text%></span></a></td>',
						'</tr>',
						'<tr>',
							'<th class="datepickerWeek"><a href="#"><span><%=weeks[3].week%></span></a></th>',
							'<td class="<%=weeks[3].days[0].classname%>"><a href="#"><span><%=weeks[3].days[0].text%></span></a></td>',
							'<td class="<%=weeks[3].days[1].classname%>"><a href="#"><span><%=weeks[3].days[1].text%></span></a></td>',
							'<td class="<%=weeks[3].days[2].classname%>"><a href="#"><span><%=weeks[3].days[2].text%></span></a></td>',
							'<td class="<%=weeks[3].days[3].classname%>"><a href="#"><span><%=weeks[3].days[3].text%></span></a></td>',
							'<td class="<%=weeks[3].days[4].classname%>"><a href="#"><span><%=weeks[3].days[4].text%></span></a></td>',
							'<td class="<%=weeks[3].days[5].classname%>"><a href="#"><span><%=weeks[3].days[5].text%></span></a></td>',
							'<td class="<%=weeks[3].days[6].classname%>"><a href="#"><span><%=weeks[3].days[6].text%></span></a></td>',
						'</tr>',
						'<tr>',
							'<th class="datepickerWeek"><a href="#"><span><%=weeks[4].week%></span></a></th>',
							'<td class="<%=weeks[4].days[0].classname%>"><a href="#"><span><%=weeks[4].days[0].text%></span></a></td>',
							'<td class="<%=weeks[4].days[1].classname%>"><a href="#"><span><%=weeks[4].days[1].text%></span></a></td>',
							'<td class="<%=weeks[4].days[2].classname%>"><a href="#"><span><%=weeks[4].days[2].text%></span></a></td>',
							'<td class="<%=weeks[4].days[3].classname%>"><a href="#"><span><%=weeks[4].days[3].text%></span></a></td>',
							'<td class="<%=weeks[4].days[4].classname%>"><a href="#"><span><%=weeks[4].days[4].text%></span></a></td>',
							'<td class="<%=weeks[4].days[5].classname%>"><a href="#"><span><%=weeks[4].days[5].text%></span></a></td>',
							'<td class="<%=weeks[4].days[6].classname%>"><a href="#"><span><%=weeks[4].days[6].text%></span></a></td>',
						'</tr>',
						'<tr>',
							'<th class="datepickerWeek"><a href="#"><span><%=weeks[5].week%></span></a></th>',
							'<td class="<%=weeks[5].days[0].classname%>"><a href="#"><span><%=weeks[5].days[0].text%></span></a></td>',
							'<td class="<%=weeks[5].days[1].classname%>"><a href="#"><span><%=weeks[5].days[1].text%></span></a></td>',
							'<td class="<%=weeks[5].days[2].classname%>"><a href="#"><span><%=weeks[5].days[2].text%></span></a></td>',
							'<td class="<%=weeks[5].days[3].classname%>"><a href="#"><span><%=weeks[5].days[3].text%></span></a></td>',
							'<td class="<%=weeks[5].days[4].classname%>"><a href="#"><span><%=weeks[5].days[4].text%></span></a></td>',
							'<td class="<%=weeks[5].days[5].classname%>"><a href="#"><span><%=weeks[5].days[5].text%></span></a></td>',
							'<td class="<%=weeks[5].days[6].classname%>"><a href="#"><span><%=weeks[5].days[6].text%></span></a></td>',
						'</tr>',
					'</tbody>'
				],
			    months: [
					'<tbody class="<%=className%>">',
						'<tr>',
							'<td colspan="2"><a href="#"><span><%=data[0]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[1]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[2]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[3]%></span></a></td>',
						'</tr>',
						'<tr>',
							'<td colspan="2"><a href="#"><span><%=data[4]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[5]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[6]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[7]%></span></a></td>',
						'</tr>',
						'<tr>',
							'<td colspan="2"><a href="#"><span><%=data[8]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[9]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[10]%></span></a></td>',
							'<td colspan="2"><a href="#"><span><%=data[11]%></span></a></td>',
						'</tr>',
					'</tbody>'
				]
			},
			defaults = {
			    flat: false,
			    starts: 1,
			    prev: '&#9664;',
			    next: '&#9654;',
			    lastSel: false,
			    mode: 'single',
			    view: 'days',
			    calendars: 1,
			    format: 'Y-m-d',
			    position: 'bottom',
			    eventName: 'click',
			    onRender: function () { return {}; },
			    onChange: function () { return true; },
			    onShow: function () { return true; },
			    onBeforeShow: function () { return true; },
			    onHide: function () { return true; },
			    locale: {
			        days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
			        daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
			        daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
			        months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
			        monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
			        weekMin: 'wk'
			    }
			},
			fill = function (el) {
			    var options = $(el).data('datepicker');
			    var cal = $(el);
			    var currentCal = Math.floor(options.calendars / 2), date, data, dow, month, cnt = 0, week, days, indic, indic2, html, tblCal;
			    cal.find('td>table tbody').remove();
			    for (var i = 0; i < options.calendars; i++) {
			        date = new Date(options.current);
			        date.addMonths(-currentCal + i);
			        tblCal = cal.find('table').eq(i + 1);
			        switch (tblCal[0].className) {
			            case 'datepickerViewDays':
			                dow = formatDate(date, 'B, Y');
			                break;
			            case 'datepickerViewMonths':
			                dow = date.getFullYear();
			                break;
			            case 'datepickerViewYears':
			                dow = (date.getFullYear() - 6) + ' - ' + (date.getFullYear() + 5);
			                break;
			        }
			        tblCal.find('thead tr:first th:eq(1) span').text(dow);
			        dow = date.getFullYear() - 6;
			        data = {
			            data: [],
			            className: 'datepickerYears'
			        }
			        for (var j = 0; j < 12; j++) {
			            data.data.push(dow + j);
			        }
			        html = tmpl(tpl.months.join(''), data);
			        date.setDate(1);
			        data = { weeks: [], test: 10 };
			        month = date.getMonth();
			        var dow = (date.getDay() - options.starts) % 7;
			        date.addDays(-(dow + (dow < 0 ? 7 : 0)));
			        week = -1;
			        cnt = 0;
			        while (cnt < 42) {
			            indic = parseInt(cnt / 7, 10);
			            indic2 = cnt % 7;
			            if (!data.weeks[indic]) {
			                week = date.getWeekNumber();
			                data.weeks[indic] = {
			                    week: week,
			                    days: []
			                };
			            }
			            data.weeks[indic].days[indic2] = {
			                text: date.getDate(),
			                classname: []
			            };
			            if (month != date.getMonth()) {
			                data.weeks[indic].days[indic2].classname.push('datepickerNotInMonth');
			            }
			            if (date.getDay() == 0) {
			                data.weeks[indic].days[indic2].classname.push('datepickerSunday');
			            }
			            if (date.getDay() == 6) {
			                data.weeks[indic].days[indic2].classname.push('datepickerSaturday');
			            }
			            var fromUser = options.onRender(date);
			            var val = date.valueOf();
			            if (fromUser.selected || options.date == val || $.inArray(val, options.date) > -1 || (options.mode == 'range' && val >= options.date[0] && val <= options.date[1])) {
			                data.weeks[indic].days[indic2].classname.push('datepickerSelected');
			            }
			            if (fromUser.disabled) {
			                data.weeks[indic].days[indic2].classname.push('datepickerDisabled');
			            }
			            if (fromUser.className) {
			                data.weeks[indic].days[indic2].classname.push(fromUser.className);
			            }
			            data.weeks[indic].days[indic2].classname = data.weeks[indic].days[indic2].classname.join(' ');
			            cnt++;
			            date.addDays(1);
			        }
			        html = tmpl(tpl.days.join(''), data) + html;
			        data = {
			            data: options.locale.monthsShort,
			            className: 'datepickerMonths'
			        };
			        html = tmpl(tpl.months.join(''), data) + html;
			        tblCal.append(html);
			    }
			},
			parseDate = function (date, format) {
			    if (date.constructor == Date) {
			        return new Date(date);
			    }
			    var parts = date.split(/\W+/);
			    var against = format.split(/\W+/), d, m, y, h, min, now = new Date();
			    for (var i = 0; i < parts.length; i++) {
			        switch (against[i]) {
			            case 'd':
			            case 'e':
			                d = parseInt(parts[i], 10);
			                break;
			            case 'm':
			                m = parseInt(parts[i], 10) - 1;
			                break;
			            case 'Y':
			            case 'y':
			                y = parseInt(parts[i], 10);
			                y += y > 100 ? 0 : (y < 29 ? 2000 : 1900);
			                break;
			            case 'H':
			            case 'I':
			            case 'k':
			            case 'l':
			                h = parseInt(parts[i], 10);
			                break;
			            case 'P':
			            case 'p':
			                if (/pm/i.test(parts[i]) && h < 12) {
			                    h += 12;
			                } else if (/am/i.test(parts[i]) && h >= 12) {
			                    h -= 12;
			                }
			                break;
			            case 'M':
			                min = parseInt(parts[i], 10);
			                break;
			        }
			    }
			    return new Date(
					y === undefined ? now.getFullYear() : y,
					m === undefined ? now.getMonth() : m,
					d === undefined ? now.getDate() : d,
					h === undefined ? now.getHours() : h,
					min === undefined ? now.getMinutes() : min,
					0
				);
			},
			formatDate = function (date, format) {
			    var m = date.getMonth();
			    var d = date.getDate();
			    var y = date.getFullYear();
			    var wn = date.getWeekNumber();
			    var w = date.getDay();
			    var s = {};
			    var hr = date.getHours();
			    var pm = (hr >= 12);
			    var ir = (pm) ? (hr - 12) : hr;
			    var dy = date.getDayOfYear();
			    if (ir == 0) {
			        ir = 12;
			    }
			    var min = date.getMinutes();
			    var sec = date.getSeconds();
			    var parts = format.split(''), part;
			    for (var i = 0; i < parts.length; i++) {
			        part = parts[i];
			        switch (parts[i]) {
			            case 'a':
			                part = date.getDayName();
			                break;
			            case 'A':
			                part = date.getDayName(true);
			                break;
			            case 'b':
			                part = date.getMonthName();
			                break;
			            case 'B':
			                part = date.getMonthName(true);
			                break;
			            case 'C':
			                part = 1 + Math.floor(y / 100);
			                break;
			            case 'd':
			                part = (d < 10) ? ("0" + d) : d;
			                break;
			            case 'e':
			                part = d;
			                break;
			            case 'H':
			                part = (hr < 10) ? ("0" + hr) : hr;
			                break;
			            case 'I':
			                part = (ir < 10) ? ("0" + ir) : ir;
			                break;
			            case 'j':
			                part = (dy < 100) ? ((dy < 10) ? ("00" + dy) : ("0" + dy)) : dy;
			                break;
			            case 'k':
			                part = hr;
			                break;
			            case 'l':
			                part = ir;
			                break;
			            case 'm':
			                part = (m < 9) ? ("0" + (1 + m)) : (1 + m);
			                break;
			            case 'M':
			                part = (min < 10) ? ("0" + min) : min;
			                break;
			            case 'p':
			            case 'P':
			                part = pm ? "PM" : "AM";
			                break;
			            case 's':
			                part = Math.floor(date.getTime() / 1000);
			                break;
			            case 'S':
			                part = (sec < 10) ? ("0" + sec) : sec;
			                break;
			            case 'u':
			                part = w + 1;
			                break;
			            case 'w':
			                part = w;
			                break;
			            case 'y':
			                part = ('' + y).substr(2, 2);
			                break;
			            case 'Y':
			                part = y;
			                break;
			        }
			        parts[i] = part;
			    }
			    return parts.join('');
			},
			extendDate = function (options) {
			    if (Date.prototype.tempDate) {
			        return;
			    }
			    Date.prototype.tempDate = null;
			    Date.prototype.months = options.months;
			    Date.prototype.monthsShort = options.monthsShort;
			    Date.prototype.days = options.days;
			    Date.prototype.daysShort = options.daysShort;
			    Date.prototype.getMonthName = function (fullName) {
			        return this[fullName ? 'months' : 'monthsShort'][this.getMonth()];
			    };
			    Date.prototype.getDayName = function (fullName) {
			        return this[fullName ? 'days' : 'daysShort'][this.getDay()];
			    };
			    Date.prototype.addDays = function (n) {
			        this.setDate(this.getDate() + n);
			        this.tempDate = this.getDate();
			    };
			    Date.prototype.addMonths = function (n) {
			        if (this.tempDate == null) {
			            this.tempDate = this.getDate();
			        }
			        this.setDate(1);
			        this.setMonth(this.getMonth() + n);
			        this.setDate(Math.min(this.tempDate, this.getMaxDays()));
			    };
			    Date.prototype.addYears = function (n) {
			        if (this.tempDate == null) {
			            this.tempDate = this.getDate();
			        }
			        this.setDate(1);
			        this.setFullYear(this.getFullYear() + n);
			        this.setDate(Math.min(this.tempDate, this.getMaxDays()));
			    };
			    Date.prototype.getMaxDays = function () {
			        var tmpDate = new Date(Date.parse(this)),
						d = 28, m;
			        m = tmpDate.getMonth();
			        d = 28;
			        while (tmpDate.getMonth() == m) {
			            d++;
			            tmpDate.setDate(d);
			        }
			        return d - 1;
			    };
			    Date.prototype.getFirstDay = function () {
			        var tmpDate = new Date(Date.parse(this));
			        tmpDate.setDate(1);
			        return tmpDate.getDay();
			    };
			    Date.prototype.getWeekNumber = function () {
			        var tempDate = new Date(this);
			        tempDate.setDate(tempDate.getDate() - (tempDate.getDay() + 6) % 7 + 3);
			        var dms = tempDate.valueOf();
			        tempDate.setMonth(0);
			        tempDate.setDate(4);
			        return Math.round((dms - tempDate.valueOf()) / (604800000)) + 1;
			    };
			    Date.prototype.getDayOfYear = function () {
			        var now = new Date(this.getFullYear(), this.getMonth(), this.getDate(), 0, 0, 0);
			        var then = new Date(this.getFullYear(), 0, 0, 0, 0, 0);
			        var time = now - then;
			        return Math.floor(time / 24 * 60 * 60 * 1000);
			    };
			},
			layout = function (el) {
			    var options = $(el).data('datepicker');
			    var cal = $('#' + options.id);
			    if (!options.extraHeight) {
			        var divs = $(el).find('div');
			        options.extraHeight = divs.get(0).offsetHeight + divs.get(1).offsetHeight;
			        options.extraWidth = divs.get(2).offsetWidth + divs.get(3).offsetWidth;
			    }
			    var tbl = cal.find('table:first').get(0);
			    var width = tbl.offsetWidth;
			    var height = tbl.offsetHeight;
			    cal.css({
			        width: width + options.extraWidth + 'px',
			        height: height + options.extraHeight + 'px'
			    }).find('div.datepickerContainer').css({
			        width: width + 'px',
			        height: height + 'px'
			    });
			},
			click = function (ev) {
			    if ($(ev.target).is('span')) {
			        ev.target = ev.target.parentNode;
			    }
			    var el = $(ev.target);
			    if (el.is('a')) {
			        ev.target.blur();
			        if (el.hasClass('datepickerDisabled')) {
			            return false;
			        }
			        var options = $(this).data('datepicker');
			        var parentEl = el.parent();
			        var tblEl = parentEl.parent().parent().parent();
			        var tblIndex = $('table', this).index(tblEl.get(0)) - 1;
			        var tmp = new Date(options.current);
			        var changed = false;
			        var fillIt = false;
			        if (parentEl.is('th')) {
			            if (parentEl.hasClass('datepickerWeek') && options.mode == 'range' && !parentEl.next().hasClass('datepickerDisabled')) {
			                var val = parseInt(parentEl.next().text(), 10);
			                tmp.addMonths(tblIndex - Math.floor(options.calendars / 2));
			                if (parentEl.next().hasClass('datepickerNotInMonth')) {
			                    tmp.addMonths(val > 15 ? -1 : 1);
			                }
			                tmp.setDate(val);
			                options.date[0] = (tmp.setHours(0, 0, 0, 0)).valueOf();
			                tmp.setHours(23, 59, 59, 0);
			                tmp.addDays(6);
			                options.date[1] = tmp.valueOf();
			                fillIt = true;
			                changed = true;
			                options.lastSel = false;
			            } else if (parentEl.hasClass('datepickerMonth')) {
			                tmp.addMonths(tblIndex - Math.floor(options.calendars / 2));
			                switch (tblEl.get(0).className) {
			                    case 'datepickerViewDays':
			                        tblEl.get(0).className = 'datepickerViewMonths';
			                        el.find('span').text(tmp.getFullYear());
			                        break;
			                    case 'datepickerViewMonths':
			                        tblEl.get(0).className = 'datepickerViewYears';
			                        el.find('span').text((tmp.getFullYear() - 6) + ' - ' + (tmp.getFullYear() + 5));
			                        break;
			                    case 'datepickerViewYears':
			                        tblEl.get(0).className = 'datepickerViewDays';
			                        el.find('span').text(formatDate(tmp, 'B, Y'));
			                        break;
			                }
			            } else if (parentEl.parent().parent().is('thead')) {
			                switch (tblEl.get(0).className) {
			                    case 'datepickerViewDays':
			                        options.current.addMonths(parentEl.hasClass('datepickerGoPrev') ? -1 : 1);
			                        break;
			                    case 'datepickerViewMonths':
			                        options.current.addYears(parentEl.hasClass('datepickerGoPrev') ? -1 : 1);
			                        break;
			                    case 'datepickerViewYears':
			                        options.current.addYears(parentEl.hasClass('datepickerGoPrev') ? -12 : 12);
			                        break;
			                }
			                fillIt = true;
			            }
			        } else if (parentEl.is('td') && !parentEl.hasClass('datepickerDisabled')) {
			            switch (tblEl.get(0).className) {
			                case 'datepickerViewMonths':
			                    options.current.setMonth(tblEl.find('tbody.datepickerMonths td').index(parentEl));
			                    options.current.setFullYear(parseInt(tblEl.find('thead th.datepickerMonth span').text(), 10));
			                    options.current.addMonths(Math.floor(options.calendars / 2) - tblIndex);
			                    tblEl.get(0).className = 'datepickerViewDays';
			                    break;
			                case 'datepickerViewYears':
			                    options.current.setFullYear(parseInt(el.text(), 10));
			                    tblEl.get(0).className = 'datepickerViewMonths';
			                    break;
			                default:
			                    var val = parseInt(el.text(), 10);
			                    tmp.addMonths(tblIndex - Math.floor(options.calendars / 2));
			                    if (parentEl.hasClass('datepickerNotInMonth')) {
			                        tmp.addMonths(val > 15 ? -1 : 1);
			                    }
			                    tmp.setDate(val);
			                    switch (options.mode) {
			                        case 'multiple':
			                            val = (tmp.setHours(0, 0, 0, 0)).valueOf();
			                            if ($.inArray(val, options.date) > -1) {
			                                $.each(options.date, function (nr, dat) {
			                                    if (dat == val) {
			                                        options.date.splice(nr, 1);
			                                        return false;
			                                    }
			                                });
			                            } else {
			                                options.date.push(val);
			                            }
			                            break;
			                        case 'range':
			                            if (!options.lastSel) {
			                                options.date[0] = (tmp.setHours(0, 0, 0, 0)).valueOf();
			                            }
			                            val = (tmp.setHours(23, 59, 59, 0)).valueOf();
			                            if (val < options.date[0]) {
			                                options.date[1] = options.date[0] + 86399000;
			                                options.date[0] = val - 86399000;
			                            } else {
			                                options.date[1] = val;
			                            }
			                            options.lastSel = !options.lastSel;
			                            break;
			                        default:
			                            options.date = tmp.valueOf();
			                            break;
			                    }
			                    break;
			            }
			            fillIt = true;
			            changed = true;
			        }
			        if (fillIt) {
			            fill(this);
			        }
			        if (changed) {
			            options.onChange.apply(this, prepareDate(options));
			        }
			    }
			    return false;
			},
			prepareDate = function (options) {
			    var tmp;
			    if (options.mode == 'single') {
			        tmp = new Date(options.date);
			        return [formatDate(tmp, options.format), tmp, options.el];
			    } else {
			        tmp = [[], [], options.el];
			        $.each(options.date, function (nr, val) {
			            var date = new Date(val);
			            tmp[0].push(formatDate(date, options.format));
			            tmp[1].push(date);
			        });
			        return tmp;
			    }
			},
			getViewport = function () {
			    var m = document.compatMode == 'CSS1Compat';
			    return {
			        l: window.pageXOffset || (m ? document.documentElement.scrollLeft : document.body.scrollLeft),
			        t: window.pageYOffset || (m ? document.documentElement.scrollTop : document.body.scrollTop),
			        w: window.innerWidth || (m ? document.documentElement.clientWidth : document.body.clientWidth),
			        h: window.innerHeight || (m ? document.documentElement.clientHeight : document.body.clientHeight)
			    };
			},
			isChildOf = function (parentEl, el, container) {
			    if (parentEl == el) {
			        return true;
			    }
			    if (parentEl.contains) {
			        return parentEl.contains(el);
			    }
			    if (parentEl.compareDocumentPosition) {
			        return !!(parentEl.compareDocumentPosition(el) & 16);
			    }
			    var prEl = el.parentNode;
			    while (prEl && prEl != container) {
			        if (prEl == parentEl)
			            return true;
			        prEl = prEl.parentNode;
			    }
			    return false;
			},
			show = function (ev) {
			    var cal = $('#' + $(this).data('datepickerId'));
			    if (!cal.is(':visible')) {
			        var calEl = cal.get(0);
			        fill(calEl);
			        var options = cal.data('datepicker');
			        options.onBeforeShow.apply(this, [cal.get(0)]);
			        var pos = $(this).offset();
			        var viewPort = getViewport();
			        var top = pos.top;
			        var left = pos.left;
			        var oldDisplay = $.curCSS(calEl, 'display');
			        cal.css({
			            visibility: 'hidden',
			            display: 'block'
			        });
			        layout(calEl);
			        switch (options.position) {
			            case 'top':
			                top -= calEl.offsetHeight;
			                break;
			            case 'left':
			                left -= calEl.offsetWidth;
			                break;
			            case 'right':
			                left += this.offsetWidth;
			                break;
			            case 'bottom':
			                top += this.offsetHeight;
			                break;
			        }
			        if (top + calEl.offsetHeight > viewPort.t + viewPort.h) {
			            top = pos.top - calEl.offsetHeight;
			        }
			        if (top < viewPort.t) {
			            top = pos.top + this.offsetHeight + calEl.offsetHeight;
			        }
			        if (left + calEl.offsetWidth > viewPort.l + viewPort.w) {
			            left = pos.left - calEl.offsetWidth;
			        }
			        if (left < viewPort.l) {
			            left = pos.left + this.offsetWidth
			        }
			        cal.css({
			            visibility: 'visible',
			            display: 'block',
			            top: top + 'px',
			            left: left + 'px'
			        });
			        if (options.onShow.apply(this, [cal.get(0)]) != false) {
			            cal.show();
			        }
			        $(document).bind('mousedown', { cal: cal, trigger: this }, hide);
			    }
			    return false;
			},
			hide = function (ev) {
			    if (ev.target != ev.data.trigger && !isChildOf(ev.data.cal.get(0), ev.target, ev.data.cal.get(0))) {
			        if (ev.data.cal.data('datepicker').onHide.apply(this, [ev.data.cal.get(0)]) != false) {
			            ev.data.cal.hide();
			        }
			        $(document).unbind('mousedown', hide);
			    }
			};
        return {
            init: function (options) {
                options = $.extend({}, defaults, options || {});
                extendDate(options.locale);
                options.calendars = Math.max(1, parseInt(options.calendars, 10) || 1);
                options.mode = /single|multiple|range/.test(options.mode) ? options.mode : 'single';
                return this.each(function () {
                    if (!$(this).data('datepicker')) {
                        options.el = this;
                        if (options.date.constructor == String) {
                            options.date = parseDate(options.date, options.format);
                            options.date.setHours(0, 0, 0, 0);
                        }
                        if (options.mode != 'single') {
                            if (options.date.constructor != Array) {
                                options.date = [options.date.valueOf()];
                                if (options.mode == 'range') {
                                    options.date.push(((new Date(options.date[0])).setHours(23, 59, 59, 0)).valueOf());
                                }
                            } else {
                                for (var i = 0; i < options.date.length; i++) {
                                    options.date[i] = (parseDate(options.date[i], options.format).setHours(0, 0, 0, 0)).valueOf();
                                }
                                if (options.mode == 'range') {
                                    options.date[1] = ((new Date(options.date[1])).setHours(23, 59, 59, 0)).valueOf();
                                }
                            }
                        } else {
                            options.date = options.date.valueOf();
                        }
                        if (!options.current) {
                            options.current = new Date();
                        } else {
                            options.current = parseDate(options.current, options.format);
                        }
                        options.current.setDate(1);
                        options.current.setHours(0, 0, 0, 0);
                        var id = 'datepicker_' + parseInt(Math.random() * 1000), cnt;
                        options.id = id;
                        $(this).data('datepickerId', options.id);
                        var cal = $(tpl.wrapper).attr('id', id).bind('click', click).data('datepicker', options);
                        if (options.className) {
                            cal.addClass(options.className);
                        }
                        var html = '';
                        for (var i = 0; i < options.calendars; i++) {
                            cnt = options.starts;
                            if (i > 0) {
                                html += tpl.space;
                            }
                            html += tmpl(tpl.head.join(''), {
                                week: options.locale.weekMin,
                                prev: options.prev,
                                next: options.next,
                                day1: options.locale.daysMin[(cnt++) % 7],
                                day2: options.locale.daysMin[(cnt++) % 7],
                                day3: options.locale.daysMin[(cnt++) % 7],
                                day4: options.locale.daysMin[(cnt++) % 7],
                                day5: options.locale.daysMin[(cnt++) % 7],
                                day6: options.locale.daysMin[(cnt++) % 7],
                                day7: options.locale.daysMin[(cnt++) % 7]
                            });
                        }
                        cal
							.find('tr:first').append(html)
								.find('table').addClass(views[options.view]);
                        fill(cal.get(0));
                        if (options.flat) {
                            cal.appendTo(this).show().css('position', 'relative');
                            layout(cal.get(0));
                        } else {
                            cal.appendTo(document.body);
                            $(this).bind(options.eventName, show);
                        }
                    }
                });
            },
            showPicker: function () {
                return this.each(function () {
                    if ($(this).data('datepickerId')) {
                        show.apply(this);
                    }
                });
            },
            hidePicker: function () {
                return this.each(function () {
                    if ($(this).data('datepickerId')) {
                        $('#' + $(this).data('datepickerId')).hide();
                    }
                });
            },
            setDate: function (date, shiftTo) {
                return this.each(function () {
                    if ($(this).data('datepickerId')) {
                        var cal = $('#' + $(this).data('datepickerId'));
                        var options = cal.data('datepicker');
                        options.date = date;
                        if (options.date.constructor == String) {
                            options.date = parseDate(options.date, options.format);
                            options.date.setHours(0, 0, 0, 0);
                        }
                        if (options.mode != 'single') {
                            if (options.date.constructor != Array) {
                                options.date = [options.date.valueOf()];
                                if (options.mode == 'range') {
                                    options.date.push(((new Date(options.date[0])).setHours(23, 59, 59, 0)).valueOf());
                                }
                            } else {
                                for (var i = 0; i < options.date.length; i++) {
                                    options.date[i] = (parseDate(options.date[i], options.format).setHours(0, 0, 0, 0)).valueOf();
                                }
                                if (options.mode == 'range') {
                                    options.date[1] = ((new Date(options.date[1])).setHours(23, 59, 59, 0)).valueOf();
                                }
                            }
                        } else {
                            options.date = options.date.valueOf();
                        }
                        if (shiftTo) {
                            options.current = new Date(options.mode != 'single' ? options.date[0] : options.date);
                        }
                        fill(cal.get(0));
                    }
                });
            },
            getDate: function (formated) {
                if (this.size() > 0) {
                    return prepareDate($('#' + $(this).data('datepickerId')).data('datepicker'))[formated ? 0 : 1];
                }
            },
            clear: function () {
                return this.each(function () {
                    if ($(this).data('datepickerId')) {
                        var cal = $('#' + $(this).data('datepickerId'));
                        var options = cal.data('datepicker');
                        if (options.mode != 'single') {
                            options.date = [];
                            fill(cal.get(0));
                        }
                    }
                });
            },
            fixLayout: function () {
                return this.each(function () {
                    if ($(this).data('datepickerId')) {
                        var cal = $('#' + $(this).data('datepickerId'));
                        var options = cal.data('datepicker');
                        if (options.flat) {
                            layout(cal.get(0));
                        }
                    }
                });
            }
        };
    } ();
    $.fn.extend({
        DatePicker: DatePicker.init,
        DatePickerHide: DatePicker.hidePicker,
        DatePickerShow: DatePicker.showPicker,
        DatePickerSetDate: DatePicker.setDate,
        DatePickerGetDate: DatePicker.getDate,
        DatePickerClear: DatePicker.clear,
        DatePickerLayout: DatePicker.fixLayout
    });
})(jQuery);

(function () {
    var cache = {};

    this.tmpl = function tmpl(str, data) {
        // Figure out if we're getting a template, or if we need to
        // load the template - and be sure to cache the result.
        var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :

        // Generate a reusable function that will serve as a template
        // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +

        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +

        // Convert the template into pure JavaScript
        str
          .replace(/[\r\t\n]/g, " ")
          .split("<%").join("\t")
          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
          .replace(/\t=(.*?)%>/g, "',$1,'")
          .split("\t").join("');")
          .split("%>").join("p.push('")
          .split("\r").join("\\'")
      + "');}return p.join('');");

        // Provide some basic currying to the user
        return data ? fn(data) : fn;
    };
})();